import type { ExprContext, Recordable } from '@/types/expression';

// 缓存编译后的表达式函数（key: 清洗后的表达式，value: 编译后的函数）
const exprCache = new Map<string, (ctx: ExprContext) => boolean>();

/**
 * 安全执行可见性表达式（支持 &&、||、_xor、()）
 * @param expr - 用户输入的表达式（如 'row.status === 0' 或 '{(row.status === 1 && row.id > 10) || index === 0}'）
 * @param context - 执行上下文（row: 行数据, index: 行索引, $root?: 全局上下文）
 * @returns 表达式执行结果（布尔值，异常时返回 false）
 */
export function executeVisibleExpr<T = Recordable>(
  expr: string | undefined | null,
  context: Omit<ExprContext<T>, '_get' | '_xor'> // 排除内置工具函数，外部无需传入
): boolean {
  // 1. 清洗表达式：去除首尾大括号、空格，空表达式直接返回 false
  const cleanExpr = expr?.replace(/^\{|\}$/g, '').trim() || '';
  if (!cleanExpr) return false;

  try {
    // 2. 从缓存获取编译后的函数（避免重复编译，提升性能）
    if (!exprCache.has(cleanExpr)) {
      // 沙箱函数：仅允许访问 context 中的变量，隔离全局环境
      const sandbox = new Function(
        'context',
        `with(context) { return Boolean(${cleanExpr}); }`
      ) as (ctx: ExprContext<T>) => boolean;
      exprCache.set(cleanExpr, sandbox);
    }

    // 3. 构造完整上下文（注入内置工具函数）
    const fullContext: ExprContext<T> = {
      ...context,
      // 安全获取嵌套属性（如 _get(row, 'ext.allowEdit', false)）
      _get: (obj, path, defaultValue) => {
        return path.split('.').reduce((o, key) => o?.[key], obj) ?? defaultValue;
      },
      // 逻辑异或实现（对布尔值友好，避免用户混淆按位异或 ^）
      _xor: (a, b) => Boolean(a) !== Boolean(b),
    };

    // 4. 执行表达式并返回结果（缓存已确保函数存在，用非空断言）
    return exprCache.get(cleanExpr)!(fullContext);
  } catch (error) {
    console.error(
      '[表达式执行失败]',
      '表达式:', cleanExpr,
      '上下文:', context,
      '错误:', error
    );
    return false; // 异常时默认隐藏按钮
  }
}